# Replication code for "Reality or Rhetoric? Between-Group Inequality and the Use of Grievance Frames on Social Media"
# Political Studies
# October 24, 2025
# Nils B. Weidmann (nils.weidmann@uni-konstanz.de)

library(tidyverse)
library(ggplot2)
library(lfe)
library(stargazer)
library(lme4)
library(kableExtra)

plot_path <- "."
table_path <- "."

data <- read.csv("analysis_org_data_preds.csv.gz")

# Generate variables
data$below_mean <- ifelse(data$nightlight_corr_pc < data$nightlight_corr_pc_avg, 1, 0)
data$below_30pct <- ifelse(data$nightlight_corr_pc/data$nightlight_corr_pc_avg < 1/3, 1, 0)
data$low_ratio <- ifelse(data$nightlight_corr_pc < data$nightlight_corr_pc_avg, data$nightlight_corr_pc_avg/data$nightlight_corr_pc, 0)
data$lineq2 <- log10(data$nightlight_corr_pc/data$nightlight_corr_pc_avg)^2
data$excluded <- ifelse(data$statusname %in% c("DISCRIMINATED", "POWERLESS", "IRRELEVANT", "SELF-EXCLUSION"), 1, 0)
data$anygrievanceframes <- ifelse(data$percentage_grievance_predicted > 0, 1, 0)
data$violence <- ifelse(data$Violence.against.government=="TRUE", 1, 0)

# Create list of organizations per country, min/max year, export as table (Table 1 in the Appendix)
data %>% select(country_name, orgname, year, total_posts) %>% group_by(country_name, orgname) %>% summarise(From=min(year), To=max(year), Posts=sum(total_posts)) %>% ungroup() %>% kbl(caption = "Example", format = "latex", booktabs = TRUE, longtable = TRUE, linesep = c('')) 

# plot histogram of the share of posts with grievances (Figure 2 in main paper)
p <- ggplot(data, aes(x=percentage_grievance_predicted)) + 
  geom_histogram(color="black", fill="white") + theme_bw() + xlab("Proportion of posts w/ grievances") + ylab("Count")
ggsave(file.path(plot_path, "histogram_grievances.pdf"), width=12, height=6, units = "cm")


# Main analysis I: Regression results, dependent variable: share of posts with grievance frames, predictors: inequality (relative status of group, political and economic)

# continuous DV, FE models, below mean IV
m1 <- felm(percentage_grievance_predicted ~ as.factor(below_mean) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m2 <- felm(percentage_grievance_predicted ~ as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m3 <- felm(percentage_grievance_predicted ~ as.factor(below_mean) + as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m4 <- felm(anygrievanceframes ~ as.factor(below_mean) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m5 <- felm(anygrievanceframes ~ as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m6 <- felm(anygrievanceframes ~ as.factor(below_mean) + as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)

# Table 1 in the main paper
stargazer(m1, m2, m3, m4, m5, m6,
          dep.var.labels = c("Proportion grievance frames", "Any grievance frames"),
          add.lines = list(c("Country FEs", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes"),
                           c("Year FEs", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes"),
                           c("Clustered SEs (group)", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes")),
          column.separate = c(3,3),
          covariate.labels=c("Poor (below average)", "Excluded", "Number of posts (log)"),
          type="latex",
          star.cutoffs = c(.1, .05, .01),
          keep.stat=c("n", "adj.rsq"),
          out = file.path(table_path, "reg_grievanceframes_OLS.tex"),
          float = F
)

# continuous DV, FE models, below 30% IV
m1 <- felm(percentage_grievance_predicted ~ as.factor(below_30pct) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m2 <- felm(percentage_grievance_predicted ~ as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m3 <- felm(percentage_grievance_predicted ~ as.factor(below_30pct) + as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m4 <- felm(anygrievanceframes ~ as.factor(below_30pct) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m5 <- felm(anygrievanceframes ~ as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)
m6 <- felm(anygrievanceframes ~ as.factor(below_30pct) + as.factor(status_excl) + log10(total_posts) | gwid + year | 0 | gwgroupid, data=data)

# Table 3 in the Appendix
stargazer(m1, m2, m3, m4, m5, m6,
          dep.var.labels = c("Proportion grievance frames", "Any grievance frames"),
          add.lines = list(c("Country FEs", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes"),
                           c("Year FEs", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes"),
                           c("Clustered SEs (group)", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes")),
          column.separate = c(3,3),
          covariate.labels=c("Poor (below 30\\%)", "Excluded", "Number of posts (log)"),
          type="latex",
          star.cutoffs = c(.1, .05, .01),
          keep.stat=c("n", "adj.rsq"),
          out = file.path(table_path, "reg_grievanceframes_30pct_OLS.tex"),
          float = F
)

# mixed effect models
m1 <- lmer(percentage_grievance_predicted ~ as.factor(below_mean) + log10(total_posts) + (1|gwid) + (1|year), data=data)
m2 <- lmer(percentage_grievance_predicted ~ as.factor(status_excl) + log10(total_posts) + (1|gwid)+ (1|year), data=data)
m3 <- lmer(percentage_grievance_predicted ~ as.factor(below_mean) + as.factor(status_excl) + log10(total_posts) + (1|gwid)+ (1|year), data=data)
m4 <- lmer(anygrievanceframes ~ as.factor(below_mean) + log10(total_posts) + (1|gwid)+ (1|year), data=data)
m5 <- lmer(anygrievanceframes ~ as.factor(status_excl) + log10(total_posts) + (1|gwid)+ (1|year), data=data)
m6 <- lmer(anygrievanceframes ~ as.factor(below_mean) + as.factor(status_excl) + log10(total_posts) + (1|gwid)+ (1|year), data=data)

# Table 4 in the Appendix
stargazer(m1, m2, m3, m4, m5, m6,
          dep.var.labels = c("Proportion grievance frames", "Any grievance frames"),
          covariate.labels=c("Poor group", "Excluded group", "Number of posts (log)"),
          add.lines = list(c("Country random effects", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes"),
                           c("Year random effects", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes")),
          column.separate = c(3,3),
          type="latex",
          star.cutoffs = c(.1, .05, .01),
          keep.stat=c("n", "adj.rsq"),
          out = file.path(table_path, "reg_grievanceframes_ML.tex"),
          float = F
)

